/// <reference lib="webworker" />
import Docxtemplater from "docxtemplater";
import PizZip from "pizzip";
import type { RenderTask, RenderResult } from "./docxTypes";
import { DOMParser, XMLSerializer } from "@xmldom/xmldom";
// ------- 合并工具（与前面给你的 patch 版相同，稍微精简） -------
// 小工具：安全取数组
const asArray = <T>(x: any): T[] => (Array.isArray(x) ? x : x == null ? [] : [x]);

// 获取某节点下所有 tagName 的后代（深度优先）
function collectDescendants(node: any, tagName: string, out: any[] = []): any[] {
  if (!node || typeof node !== "object") return out;
  for (const [k, v] of Object.entries(node)) {
    if (k === tagName) {
      out.push(...asArray(v));
    }
    const arr = asArray(v);
    for (const child of arr) {
      if (child && typeof child === "object") collectDescendants(child, tagName, out);
    }
  }
  return out;
}

// 取 <w:tc> 的纯文本（拼接所有 <w:t>）
function getTcText(tc: any): string {
  const texts = collectDescendants(tc, "w:t").map((t: any) =>
    typeof t === "string" ? t : t?.["#text"] ?? ""
  );
  return texts.join("").trim();
}

// 确保有 w:tcPr
function ensureTcPr(tc: any) {
  if (!tc["w:tcPr"]) tc["w:tcPr"] = {};
  return tc["w:tcPr"];
}

// 清空单元格内容，留一个空段落 <w:p/>
function clearTcKeepEmptyParagraph(tc: any) {
  // 删除所有 w:p
  delete tc["w:p"];
  // 放一个空段落；多数解压器接受对象或空数组形式
  tc["w:p"] = [{}];
}

// 在解析后的对象上，对目标表进行纵向合并
function mergeTableColumnsByHeader(
  jdoc: any,
  headerTitles: string[],
  mergeHeaderTexts: string[]
) {
  // Word 主体：w:document -> w:body
  const body = jdoc["w:document"]?.["w:body"];
  if (!body) return;

  // 找所有 w:tbl（包括被包在 sdt 等结构里的表）
  const allTables = collectDescendants(body, "w:tbl");
  if (!allTables.length) return;

  // 逐表尝试匹配表头
  let targetTbl: any | undefined;
  let rows: any[] = [];
  for (const tbl of allTables) {
    const trList = asArray(tbl["w:tr"]);
    if (!trList.length) continue;
    const headerRow:any = trList[0];
    const headerTcs = asArray(headerRow?.["w:tc"]);
    if (headerTcs.length < headerTitles.length) continue;

    let match = true;
    for (let i = 0; i < headerTitles.length; i++) {
      if (getTcText(headerTcs[i]) !== headerTitles[i]) {
        match = false; break;
      }
    }
    if (match) {
      targetTbl = tbl;
      rows = trList;
      break;
    }
  }
  if (!targetTbl || rows.length <= 1) return;

  // 通过表头文字确定要合并的列索引
  const headerCells = asArray(rows[0]["w:tc"]);
  const colIdxToMerge: number[] = [];
  for (const name of mergeHeaderTexts) {
    const idx = headerCells.findIndex((tc: any) => getTcText(tc) === name);
    if (idx >= 0) colIdxToMerge.push(idx);
  }
  if (!colIdxToMerge.length) return;

  // 对每个目标列做 vMerge：第 1 数据行 restart，其后 continuation
  for (const colIdx of colIdxToMerge) {
    for (let r = 1; r < rows.length; r++) {
      const tcs = asArray(rows[r]["w:tc"]);
      if (tcs.length <= colIdx) continue;
      const tc = tcs[colIdx];
      const pr = ensureTcPr(tc);

      // 移除已有 w:vMerge
      if (pr["w:vMerge"]) delete pr["w:vMerge"];

      // 设置 w:vMerge；首行 restart，其余空对象（续行）
      if (r === 1) {
        pr["w:vMerge"] = { "@_w:val": "restart" };
      } else {
        pr["w:vMerge"] = {}; // continuation
        clearTcKeepEmptyParagraph(tc);
      }
    }
  }
}

// -----------------------------------------------------------------

self.onmessage = async <T>(e: MessageEvent<RenderTask<T>>) => {
  const { id, templateBuffer, data, merge } = e.data;
  try {
    const zip = new PizZip(templateBuffer);
    const doc = new Docxtemplater(zip, {
      paragraphLoop: true,
      linebreaks: true,
      nullGetter: () => "",
    });
    doc.setData(data as Record<string, unknown>);
    doc.render();

    // ✅ 渲染后可选合并（稳健：失败也不影响导出）
    if (merge?.enabled) {
      try {
        const path = "word/document.xml";
        const f = doc.getZip().file(path);
        if (!f) throw new Error("word/document.xml not found in zip");

        const xml = f.asText();

        // 1) 解析（xmldom 在 Worker 环境可用）
        const xdoc:any = new DOMParser().parseFromString(xml, "application/xml");

        // 2) 工具 & 命名空间
        const W:string = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
        const cellText = (tc: Element) => {
          const ts:any = xdoc.getElementsByTagNameNS
            ? tc.getElementsByTagNameNS(W, "t")
            : (tc as any).getElementsByTagName("w:t");
          let s = "";
          for (let i = 0; i < ts.length; i++) s += ts[i].textContent ?? "";
          return s.trim();
        };
        const ensureTcPr:any = (tc: Element) => {
          let pr:any = tc.getElementsByTagNameNS(W, "tcPr")[0];
          if (!pr) {
            pr = xdoc.createElementNS(W, "w:tcPr");
            tc.insertBefore(pr, tc.firstChild);
          }
          return pr;
        };
        const clearCellKeepEmptyParagraph = (tc: Element) => {
          // 删掉所有 <w:p>
          const ps = Array.from(tc.getElementsByTagNameNS(W, "p"));
          ps.forEach((p) => p.parentNode?.removeChild(p));
          // 放一个空段落，避免版式异常
          tc.appendChild(xdoc.createElementNS(W, "w:p"));
        };

        // 3) 按表头定位表格（不改变任何结构/顺序）
        const headerTitles = merge.headerTitles ?? ["项目", "起止时间", "金额", "合计", "收款单位名称"];
        const mergeHeaderTexts = merge.mergeHeaderTexts ?? ["合计", "收款单位名称"];

        const tables = Array.from(xdoc.getElementsByTagNameNS(W, "tbl"));
        const target:any = tables.find((tbl:any) => {
          const rows = tbl.getElementsByTagNameNS(W, "tr");
          if (!rows.length) return false;
          const th = rows[0].getElementsByTagNameNS(W, "tc");
          if (th.length < headerTitles.length) return false;
          for (let i = 0; i < headerTitles.length; i++) {
            if (cellText(th[i]) !== headerTitles[i]) return false;
          }
          return true;
        });

        if (target) {
          const rows:any = Array.from(target.getElementsByTagNameNS(W, "tr"));
          if (rows.length > 1) {
            const headerCells = Array.from(rows[0].getElementsByTagNameNS(W, "tc"));
            const colIdxToMerge = mergeHeaderTexts
              .map((t) => headerCells.findIndex((tc:any) => cellText(tc) === t))
              .filter((i) => i >= 0);

            for (const colIdx of colIdxToMerge) {
              for (let r = 1; r < rows.length; r++) {
                const tcs = rows[r].getElementsByTagNameNS(W, "tc");
                if (tcs.length <= colIdx) continue;
                const tc = tcs[colIdx];

                const pr = ensureTcPr(tc);
                const old = pr.getElementsByTagNameNS(W, "vMerge")[0];
                if (old) pr.removeChild(old);

                const vMerge = xdoc.createElementNS(W, "w:vMerge");
                if (r === 1) vMerge.setAttributeNS(W, "w:val", "restart");
                pr.appendChild(vMerge);

                if (r > 1) clearCellKeepEmptyParagraph(tc);
              }
            }
          }
        } else {
          // 没命中目标表，直接跳过（不影响导出）
          console.warn("[merge] target table not found, skip merge.");
        }

        // 4) 序列化：保持原有节点顺序
        const patched = new XMLSerializer().serializeToString(xdoc);
        doc.getZip().file(path, patched);
      } catch (e) {
        console.warn("[merge] failed, skip merge:", e);
      }
    }

    const arrayBuffer = doc.getZip().generate({
      type: "arraybuffer",
      mimeType:
        "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
      compression: "DEFLATE",
    }) as ArrayBuffer;

    (self as unknown as Worker).postMessage(
      { id, ok: true, arrayBuffer, byteLength: arrayBuffer.byteLength } as RenderResult,
      [arrayBuffer]
    );
  } catch (err: any) {
    const explanation =
      err?.properties?.errors?.map((x: any) => x.properties?.explanation).join("\n") ||
      err?.message ||
      String(err);
    (self as unknown as Worker).postMessage({ id, ok: false, error: explanation } as RenderResult);
  }
};
